package com.cd.card.service.impl;

import com.alibaba.fastjson2.JSONArray;
import com.cd.card.domain.Inventory;
import com.cd.card.domain.LqqCardRecord;
import com.cd.card.dto.InventoryDto;
import com.cd.card.dto.RankListDto;
import com.cd.card.dto.RecordDto;
import com.cd.card.mapper.InventoryMapper;
import com.cd.card.repository.LqqCardRecordRepository;
import com.cd.common.constant.CacheConstants;
import com.cd.common.constant.Constants;
import com.cd.common.domain.SysUser;
import com.cd.common.exception.LqqException;
import com.cd.common.util.AuthUtils;
import com.cd.common.util.RedisCache;
import com.cd.card.domain.LqqCard;
import com.cd.card.domain.LqqCardUser;
import com.cd.card.mapper.LqqCardMapper;
import com.cd.card.mapper.LqqCardUserMapper;
import com.cd.card.service.CardService;
import com.cd.system.jpa.entity.DailyUserCount;
import com.cd.system.service.UserService;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;

@Service
//@Scope("prototype") //可以把该实例变成多例
public class CardServiceImpl implements CardService {

    private static final Logger logger = LoggerFactory.getLogger(CardServiceImpl.class);

    @Autowired
    private LqqCardMapper lqqCardMapper;

    @Autowired
    private LqqCardRecordRepository lqqCardRecordRepository;

    @Autowired
    private UserService userService;

    @Autowired
    private InventoryMapper inventoryMapper;

    @PersistenceContext
    private EntityManager entityManager;


//    @Value("${lqq.card.fourRate}")
//    private double fourRate;
//
//    @Value("${lqq.card.fiveRate}")
//    //五星概率
//    private double fiveRate;
//
//    @Value("${lqq.card.sixRate}")
//    //6星概率
//    private double sixRate;
//
//    //抽奖基数
//    private int cardinalNumber=100000;

    @Value("${lqq.card.rate}")
    private int[] rate;

    class cardLevel {
        static final int SIX_STAR = 6;
        static final int FIVE_STAR = 5;
        static final int FOUR_STAR = 4;
        static final int THREE_STAR = 3;
    }

    @Autowired
    private LqqCardUserMapper lqqCardUserMapper;

    @Autowired
    private RedisCache redisCache;

    @Override
    public List<LqqCard> getAllCardList(LqqCard lqqCard) {
        //如果有分页数据 单独查询
        return lqqCardMapper.selectCardList(lqqCard);

    }

    //算法详解
    //先确定品质，然后动态计算物品

    /**
     * @Description 抽卡
     * 首先确定抽的品质，0-9399为紫色 9340-9998为金色，9999为纯金
     * 然后确定各个卡牌的数量，算出值 动态增加 紫色卡牌为1开头100001，金色2开 红色3开
     * 确认用户的保底次数 if保底 必出 if大保底 必出
     * @Author lqq
     * @Param num 抽卡次数
     * @Param mode 类型 0-普通 1-限定
     **/
    @Override
    @Transactional
    public Map<String, Object> drawCard(Long num, String mode) {
//        switch (mode) {
//            case "0":
//                break;
//            case "1":
//                break;
//        }
        //查记录 有记录就走记录，没有新建用户
        LqqCardUser user = null;
        List<LqqCardUser> userList = lqqCardUserMapper.selectCardUserList(new LqqCardUser(AuthUtils.currentUserId()));
        if (userList.size() > 0) {
            user = userList.get(0);
        } else {//已经换到其他地方新建了，这里按理来说不会走 参考getCardUserInfo
            user = new LqqCardUser();
            user.setSmallGuarantee(0);
            user.setBigGuarantee(0);
            user.setUserId(AuthUtils.currentUserId());
            user.setAllDrawTimes(0L);
            user.setDrawTicket(100L);//新用户送一百
            logger.info("该用户为绕过了机制的新用户。id：{}", user.getUserId());
            user.setBigGuaranteeFlag(0);
            user.setCreateTime(new java.util.Date());
            lqqCardUserMapper.insertCardUser(user);
        }
        if (user.getDrawTicket() < num) {
            throw new LqqException("奖券不足");
        }
        List<LqqCard> cardList = this.getAllCardList(new LqqCard());
        //做一份缓存
        //角色池 后续放到一个固定地方 加个refresh 减少垃圾回收 加个时间戳或版本戳 如版本version 这个version是多少 对比 不一致则刷新
        //key后面加个版本时间联合key 定时任务删除往期缓存
        Map<Integer, List<LqqCard>> cardPool = new LinkedHashMap<>();
        for (LqqCard card : cardList) {
            if (cardPool.get(card.getLevel()) == null) {
                cardPool.put(card.getLevel(), new ArrayList<>());
                cardPool.get(card.getLevel()).add(card);
            }
            cardPool.get(card.getLevel()).add(card);
        }
        //先确定品质 然后再去大池子里捞对应物品
        Random random = new Random();
        Map<String, Object> result = new LinkedHashMap<>();
        List<LqqCard> drawCardList = new LinkedList<>();
        for (Long i = 0L; i < num; i++) {
            //确定拿到的品质 先抽一张 然后再去对比保底 最后对比角色
            int cardinalNumber = Arrays.stream(rate).sum();
            //随机抽取
            int randomLevel = random.nextInt(cardinalNumber);
            //获得对应的品质
            Long typeId = getCardByRandomNumber(randomLevel);
            //如果有保底替换保底 这里设计可能与问题 应该在进门就判断
            Integer cardTypes = replaceCardIdByGuarantee(typeId, user);
            //领对应的品质池
            List<LqqCard> lqqCards = cardPool.get(cardTypes);
            //随机抽
            LqqCard drawCard = lqqCards.get(random.nextInt(lqqCards.size()));
            drawCardList.add(drawCard);
            //如果吃过保底 直接变成当期卡牌
        }
        result.put("record", drawCardList);
        //设置剩余券
        user.setDrawTicket(user.getDrawTicket() - num);
        //奖励放入背包
        //先生成对应的物品和数量，再一起加入
        HashMap<Long, Long> cardMap = new HashMap<>();
        for (LqqCard card : drawCardList) {
            if (cardMap.get(card.getCardId()) == null) {
                cardMap.put(card.getCardId(), 1L);
            } else {
                cardMap.put(card.getCardId(), cardMap.get(card.getCardId()) + 1);
            }
        }
//        for (LqqCard card : drawCardList) {
//            cardMap.merge(card.getCardId(), 1L, Long::sum);
//        }
        //依次插入 bug：会造成死锁
        for (Map.Entry<Long, Long> entry : cardMap.entrySet()) {
            Inventory inventory = new Inventory();
            inventory.setCardId(entry.getKey());
            inventory.setUserId(user.getUserId());
            inventory.setQuantity(entry.getValue());
            inventoryMapper.upsertInventory(inventory);
        }
        //记录次数
        user.setAllDrawTimes(user.getAllDrawTimes() + num);
        lqqCardUserMapper.updateCardUser(user);
        result.put("userInfo", user);
        //如果十连抽 额外记录欧气值排序 舍弃出金率的说法 不落库

        Long sum = drawCardList.stream().mapToLong(LqqCard::getScore).sum();
        //logger.info("十连抽欧气值：" + sum);
        //redis write zrangebyscore books -inf 8.91 withscores
        LqqCardRecord record = record(drawCardList, num);
        //value + record的id
        //如果十连抽 额外记录欧气值排序 舍弃出金率的说法 不落库
        if (drawCardList.size() == 10) {
            redisCache.setCacheZSet(CacheConstants.CARD_RES, record.getRecordId().toString(), sum);
        }
        result.put("score", sum);

        return result;
}

    private LqqCardRecord record(List<LqqCard> drawCardList, Long num) {
        LqqCardRecord record = new LqqCardRecord();
        //记录每个卡片的id
        String result = drawCardList.stream()
                .map(LqqCard::getCardId)
                .map(Object::toString)
                .collect(Collectors.joining(","));
        record.setCardContent(result);
        record.setUserId(AuthUtils.currentUserId());
        record.setUserName(AuthUtils.currentUsername());
        String drawType = "";
        if (num == 1) {
            drawType = Constants.SINGLE_DRAW;
        } else if (num == 10) {
            drawType = Constants.TEN_DRAW;
        } else {
            drawType = num + "抽";
        }
        record.setDrawType(drawType);
        record.setDrawTime(new Date());
        LqqCardRecord save = lqqCardRecordRepository.save(record);
        return save;
    }

    @Override
    public LqqCardUser getCardUserInfo() {
        List<LqqCardUser> userList = lqqCardUserMapper.selectCardUserList(new LqqCardUser(AuthUtils.currentUserId()));
        if (userList.size() < 1) {
            LqqCardUser user = new LqqCardUser();
            user.setSmallGuarantee(0);
            user.setBigGuarantee(0);
            user.setUserId(AuthUtils.currentUserId());
            user.setAllDrawTimes(0L);
            user.setDrawTicket(1000000L);//新用户送一千抽 划掉 一百万 测试服务器性能+刷榜
            user.setBigGuaranteeFlag(0);
            user.setCreateTime(new java.util.Date());
            lqqCardUserMapper.insertCardUser(user);
            return user;
        }
        return userList.get(0);
    }

    @Override
    public List<RankListDto> getRankList() {
        //这个的返回值应该是什么
        Set<ZSetOperations.TypedTuple<String>> cacheZSet = redisCache.getCacheZSet(CacheConstants.CARD_RES, 0, 0);
        List<RankListDto> list = new LinkedList<>();
        for (ZSetOperations.TypedTuple<String> typedTuple : cacheZSet) {
            RankListDto rankListDto = new RankListDto();
            Optional<LqqCardRecord> optionalRecord = lqqCardRecordRepository.findById(Long.valueOf(typedTuple.getValue()));
            if (optionalRecord.isPresent()) {//数据库被删但是redis还存在的情况，这里会关联不到
                LqqCardRecord lqqCardRecord = optionalRecord.get();
                rankListDto.setUserName(lqqCardRecord.getUserName());
                //如果有昵称，则添加昵称
                SysUser user = userService.findByUsername(lqqCardRecord.getUserName());
                if (user != null && user.getNickName() != null) {
                    rankListDto.setUserName(lqqCardRecord.getUserName() + "(" + user.getNickName() + ")");
                }
                //依次添加分数和时间
                rankListDto.setValue(Double.valueOf(typedTuple.getScore()));
                rankListDto.setDate(lqqCardRecord.getDrawTime());
                list.add(rankListDto);
            }
        }
        //欧气值从大到小 如果有相同的 按照时间从先到后
        Collections.sort(list, new Comparator<RankListDto>() {
            @Override
            public int compare(RankListDto o1, RankListDto o2) {
                //return o2.getValue().compareTo(o1.getValue());
                //反着是倒序
                int compare = Double.compare(o2.getValue(), o1.getValue());
                if (compare == 0) {
                    return Long.compare(o1.getDate().getTime(), o2.getDate().getTime());
                }
                return compare;
            }
        });
        return list;
    }


    @Override
    public List<InventoryDto> getInventory() {
        List<InventoryDto> inventoryList = inventoryMapper.selectInventoryList(AuthUtils.currentUserId());
        return inventoryList;
    }

    @Override
    @PreAuthorize("@ss.hasPermi('ROLE_ADMIN')")
    public String clearHistory() {
        if (AuthUtils.currentUserId() != 1L) {
            return "您不是超级管理员";
        }
        //清空背包
        inventoryMapper.deleteAll();
        //清空记录
        lqqCardRecordRepository.deleteAll();
        //清空排行榜
        redisCache.deleteObject(CacheConstants.CARD_RES);
        return "清理完毕";
    }

    /**
     * @Description 仅显示最近二十次抽卡记录 无论单还是十抽
     * 如需完整的后续扩展完整抽卡查看 注意加时间过滤
     * 记录倒排
     * @Author lqq
     * @Date 2025/4/9 10:26
     **/
    @Override
    public List<RecordDto> getRecord() {
        //2800条三秒 严重的性能瓶颈
        //List<LqqCardRecord> recordList = lqqCardRecordRepository.findByUserId(AuthUtils.currentUserId());

        String sql = "SELECT * FROM lqq_card_record lcr ORDER BY lcr.draw_time DESC LIMIT 20"; // 原生 SQL 查询
        Query query = entityManager.createNativeQuery(sql, LqqCardRecord.class);
        List<LqqCardRecord> recordList=query.getResultList();
        //转化
        List<RecordDto> recordDtoList = new ArrayList<>();
        if (recordList.size() > 0) {
            for (LqqCardRecord record : recordList) {

                String cardContent = record.getCardContent();

                for (String cardId : cardContent.split(",")) {
                    RecordDto recordDto = new RecordDto();
                    List<LqqCard> allCardList = getAllCardList(new LqqCard());
                    LqqCard card = allCardList.stream().filter(c -> c.getCardId().equals(Long.valueOf(cardId))).findFirst().get();
                    recordDto.setCardName(card.getCardName());
                    recordDto.setLevel(card.getLevel());
                    recordDto.setDrawType(record.getDrawType());
                    recordDto.setDrawTime(record.getDrawTime().toString());
                    recordDtoList.add(recordDto);
                }
            }
        }
        return recordDtoList;
    }

    //根据保底获取卡牌id
    private int replaceCardIdByGuarantee(Long typeId,
                                         LqqCardUser lqqCardUser) {
        //大小保底++
        lqqCardUser.setSmallGuarantee(lqqCardUser.getSmallGuarantee() + 1);
        lqqCardUser.setBigGuarantee(lqqCardUser.getBigGuarantee() + 1);
        //如果保底优先保底
        if (lqqCardUser.getBigGuarantee() == 80) {
            lqqCardUser.setBigGuarantee(0);
            lqqCardUser.setSmallGuarantee(0);
            if (typeId <= 5L) return cardLevel.FIVE_STAR;
        }
        if (lqqCardUser.getSmallGuarantee() == 10) {
            lqqCardUser.setSmallGuarantee(0);
            if (typeId <= 4L) return cardLevel.FOUR_STAR;
        }
        //否则记录一下，正常返回
        if (typeId == 4L) {
            lqqCardUser.setSmallGuarantee(0);
            return cardLevel.FOUR_STAR;
        } else if (typeId == 5L) {
            lqqCardUser.setBigGuarantee(0);
            lqqCardUser.setSmallGuarantee(0);
            return cardLevel.FIVE_STAR;
        } else if (typeId == 6L) {
            lqqCardUser.setSmallGuarantee(0);
            lqqCardUser.setBigGuarantee(0);
            return cardLevel.SIX_STAR;
        } else if (typeId == 3L) {
            return cardLevel.THREE_STAR;
        }
        throw new LqqException("非法卡牌");
    }

    //动态计算卡牌id
    private Long getCardByRandomNumber(int starLevel) {
        //logger.info("rate :[{}]", rate);
        if (starLevel < rate[0]) {
            //三星
            return 3L;
        } else if (starLevel < rate[0] + rate[1]) {
            return 4L;
        } else if (starLevel < rate[1] + rate[2] + rate[0]) {
            return 5L;
        } else if (starLevel < 2100000000) {//值为总和 这里偷懒写大点 方便加七星八星扩展
            return 6L;
        } else {
            throw new LqqException("非法卡牌");
        }
    }


}